/*
 * multiple architecture C decompiler based on libbfd
 * $Id: main.c,v 1.24 2012/07/21 00:05:04 curt Exp $
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include "decomp.h"
#include "getopt.h"

#include "bfd.h"

char *pname;

label *labels;
label *labeltail;

section *sects;
function *funcs = NULL;
function *curfunc;

int imax = -1;
int bias = 0;
int verbose = 0;
int directive = 0;

section_check_size(section *sp, int size)
{
    int i;

    if (size <= sp->length) {
        return;
    }

    if (sp->length) {
        sp->labels = realloc(sp->labels, size * sizeof(label *));
        sp->refs = realloc(sp->refs, size * sizeof(reference *));
        sp->insts = realloc(sp->insts, size * sizeof(inst *));
    } else {
        sp->labels = malloc(size * sizeof(label *));
        sp->refs = malloc(size * sizeof(reference *));
        sp->insts = malloc(size * sizeof(inst *));
    }

    for (i = sp->length; i < size; i++) {
        sp->labels[i] = NULL;
        sp->refs[i] = NULL;
        sp->insts[i] = NULL;
    }
    sp->length = size;
}

/*
 * return a section, given the name, creating it if needed
 */
section *
get_section(const char *name)
{
    char *s;
    char c;
    section *sp;
    static unsigned int avail_prefixes = 0x3ffffff;

    for (sp = sects; sp; sp = sp->next) {
        if (strcmp(sp->name, name) == 0) {
            return (sp);
        }
    }

    sp = malloc(sizeof(*sp));

    sp->name = strdup(name);
    sp->text = 0;

    /*
     * make a decent unique prefix,
     * pick the first unused letter in the section name
     * otherwise, the first unused letter overall
     */
    for (s = name; *s; s++) {
        c = *s | 0x20;
        if (c >= 'a' && c <= 'z') {
            if (avail_prefixes & (1 << (c - 'a')))
                break;
        }
    }
    if (!*s) {
        for (c = 'a'; c <= 'z'; c++) {
            if (avail_prefixes & (1 << (c - 'a')))
                break;
        }
    }
    sp->prefix = c;
    avail_prefixes ^= (1 << (c - 'a'));

    sp->data = NULL;
    sp->length = 0;
    sp->labels = NULL;
    sp->refs = NULL;
    sp->insts = NULL;
    sp->next = sects;
    sects = sp;

    return (sp);
}

/*
 * return a label for a section and offset
 */
label *
lookup_label(section *sp, int offset)
{
    section_check_size(sp, offset+1);
    return sp->labels[offset];
}

/*
 * return a reference contained at a section and offset
 */
reference *
lookup_reference(section *sp, int offset, int index)
{
    section_check_size(sp, offset+1);
    return sp->refs[offset];
}

/*
 * return an instruction within a section and offset
 */
inst *
lookup_inst(section *sp, int offset)
{
    section_check_size(sp, offset+1);
    return sp->insts[offset];
}

/*
 * construct an instruction and link it in to everything
 * zero everything else
 */
inst *
make_inst(section *sp, int offset)
{
    inst *ip;
    int i;

    ASSERT((lookup_inst(sp, offset) == 0));

    ip = malloc(sizeof(*ip));

    ip->sect = sp;
    ip->offset = offset;

    ip->next = NULL;

    /* is there a label for this instruction */
    ip->addr = lookup_label(sp, offset);
    if (ip->addr) {
        ASSERT((ip->addr->inst == 0));
        ip->addr->inst = ip;
    }

    section_check_size(sp, offset+1);
    sp->insts[offset] = ip;

    ip->opcode = 0;
    ip->type = 0;

    for (i = 0; i < MAXATTR; i++) {
        ip->attr[i] = 0;
    }
    /* find references for this instruction */
    for (i = 0; i < MAXREFS; i++) {
        ip->ref[i] = lookup_reference(sp, offset, i);
        if (ip->ref[i]) {
            ip->ref[i]->inst = ip;
        }
    }
    ip->calc = 0;
    ip->state = 0;
    ip->refcnt = 0;
    ip->func = curfunc;
    for (i = 0; i < MAXFLOWS; i++) {
        ip->flow[i] = NULL;
    }
    return (ip);
}

/*
 * make a label.  if it already exists, return it.
 * if we pass in a null name, generate an appropriate name
 */
label *
make_label(section *sp, int offset, char *name)
{
    label *lp;
    inst *ip;

    lp = lookup_label(sp, offset);
    if (lp) {
        return (lp);
    }
    lp = malloc(sizeof(*lp));
    lp->sect = sp;
    lp->offset = offset;
    lp->generated = 0;

    if (!name) {
        static char namebuf[10];
        sprintf(namebuf, "%c_%x", sp->prefix, offset);
        name = namebuf;
        lp->generated = 1;
    }
    lp->name = strdup(name);

    lp->refs = NULL;
    lp->g_next = 0;

    if (labels) {
        labeltail->g_next = lp;
    } else {
        labels = lp;
    }
    labeltail = lp;

    section_check_size(sp, offset+1);
    sp->labels[offset] = lp;

    /* if there's an inst already there, hook it in */
    ip = lookup_inst(sp, offset);
    if (ip) {
        /* since we missed lookup_label */
        ASSERT((ip->addr == 0));
        ip->addr = lp;
    }
    lp->inst = ip;
    if (verbose & VERBOSE_FUNC) {
        printf("make_label: %s %s:0x%x\n", name, sp->name, offset);
    }
    return (lp);
}

/*
 * make a reference and attach it to the section, label
 * and instruction
 */
reference *
make_reference(section *sp, int offset, int index, label *whereto)
{
    reference *rp;
    inst *dip;

    ASSERT((lookup_reference(sp, offset, index) == 0));

    rp = malloc(sizeof(*rp));
    rp->sect = sp;
    rp->offset = offset;

    rp->place = whereto;
    rp->howcode = 0;
    rp->index = index;

    /* cross-references */
    rp->l_next = whereto->refs;
    whereto->refs = rp;

    section_check_size(sp, offset+1);
    sp->refs[offset] = rp;

    /* attach to instruction if it exists */
    dip = lookup_inst(sp, offset);
    if (dip) {
        ASSERT((dip->ref[index] == 0));
        dip->ref[index] = rp;
    }
    if (verbose & VERBOSE_FUNC) {
        printf("make_reference: %s(%x:%d) to %s\n",
                sp->name, offset, index, whereto->name);
    }
    return (rp);
}

void
dump_reference(reference *rp, char *prefix)
{
    if (!prefix) prefix = "reference: ";
    printf("%s%s:0x%x to %s using %x\n",
	   prefix, rp->sect->name, rp->offset, rp->place->name, rp->howcode);
}

void
dump_label(label *lp, int xrefs)
{
    reference *rp;

    printf("label: %s %s:0x%x\n", lp->name, lp->sect->name, lp->offset);
    if (xrefs) {
        for (rp = lp->refs; rp; rp = rp->l_next) {
            dump_reference(rp, "\txref: ");
        }
    }
}

void
dump_labels()
{
    label *lp;

    for (lp = labels; lp; lp = lp->g_next) {
        dump_label(lp, 1);
    }
}

void
dump_section(section *sp)
{
    label *lp;
    reference *rp;
    int i;
    int span;
    int base;

    printf("section%s: %s: size: %d\n", sp->text ? "(text)" : "",
        sp->name, sp->length);

    if (sp->text) {
        inst *ip;
        for (i = 0; i < sp->length; i++) {
            ip = lookup_inst(sp, i);
            if (!ip) continue;
            lp = lookup_label(sp, ip->offset);
            if (lp) {
                printf("%s:\n", lp->name);
            }
            dump_inst(ip);
        }
    } else {

        base = 0;
        span = 0;
        for (i = 0; i < sp->length; i++) {
	    lp = lookup_label(sp, i);
        rp = lookup_reference(sp, i, 0);
	    if (lp || rp) {
                if (span) {
                    dump_bytes(sp->data, base, span+1);
                    span = 0;
                }
		if (lp) {
                    printf("%s:\n", lp->name);
                }
                if (rp) {
                    if (rp->howcode == 0x20) {
                        printf("    ADDR(%s)\n", rp->place->name);
                    } else {
                        dump_reference(rp, "");
                    }
                }
                base = i;
	    } else {
                span++;
            }
        }
        if (span) {
            dump_bytes(sp->data, base, span+1);
        }
    }
}

void
dump_sections()
{
    section *sp;

    for (sp = sects; sp; sp = sp->next) {
        dump_section(sp);
    }
}

void
free_decomp()
{
    int i;
    section *sp, *fsp;

    sp = sects;
    while (sp) {
        free(sp->name);
        for (i = 0; i < sp->length; i++) {
            if (sp->insts[i]) free(sp->insts[i]);
            if (sp->labels[i]) {
                free(sp->labels[i]->name);
                free(sp->labels[i]);
            }
            if (sp->refs[i]) free(sp->refs[i]);
        }
        fsp = sp;
        sp = sp->next;
        free(fsp);
    }
    sects = NULL;
    labels = NULL;
}

/*
 * create a code node and add it to the current function
 */
code *
make_code(code_e type)
{
    function *fp;
    code *cp;
    fp = funcs;

    cp = malloc(sizeof (*cp));
    cp->type = type;
    cp->next = NULL;
    cp->arg1 = NULL;
    cp->arg2 = NULL;

    if (fp->code) {
        fp->tail->next = cp;
    } else {
        fp->code = cp;
    }
    fp->tail = cp;

    return (cp);
}

char *codename[] = {
    "call", "store", "if"
};

void
dump_code(code *cp)
{
    expr *ep;

    switch(cp->type) {
    case C_CALL:
        /* TODO - function pointers */
        if (cp->arg1->op == O_SYMBOL) {
            printf("    %s(", cp->arg1->symbol->name);
            for (ep = cp->arg2; ep; ep = ep->next) {
                dump_expr(ep, 2, "arg");
            }
            printf(");");
        }
        break;
    case C_STORE:
        dump_expr(cp->arg1, 1, "dest");
        printf(" = ");
        dump_expr(cp->arg2, 1, "src");
        printf(";");
        break;
    case C_IF:
        break;
    default:
        printf("%s", codename[cp->type]);
        if (cp->arg1) {
            dump_expr(cp->arg1, 1, "arg1");
        }
        if (cp->arg2) {
            dump_expr(cp->arg2, 1, "arg2");
        }
        break;
    }
    printf("\n");
}

char *opname[] = {
    "O_REG",
    "O_CONST", "O_SYMBOL", "O_RETADDR", "O_STACK", "O_ARG",
    "O_LOAD", "O_NOT", "O_NEG",
    "O_OR", "O_AND", "O_XOR",
    "O_ADD", "O_SUB", "O_MUL", "O_DIV",
    "O_LSHIFT", "O_RSHIFT",
    "O_LAND", "O_LOR", "O_LNOT",
};

expr *
make_expr(operator_e type, expr *left, expr *right, label *symbol, unsigned long long cv)
{
    expr *ep;
    int error = 0;
#define EXERR_EXTRA 1
#define EXERR_MISSING 2

    ep = malloc(sizeof (*ep));
    ep->left = left;
    ep->right = right;
    ep->symbol = symbol;
    ep->op = type;
    ep->val = cv;
    ep->width = 64;

    switch (type) {
    /* special operators */
    case O_REG:
    case O_CONST:
    case O_ARG:
        if (left || right || symbol) error = EXERR_EXTRA;
        break;
    case O_SYMBOL:
        if (left || right || cv) error = EXERR_EXTRA;
        if (!symbol) error |= EXERR_MISSING;
        break;
    case O_RETADDR: case O_STACK:
        if (left || right || symbol || cv) error = EXERR_EXTRA;
        break;
    /* unary operators */
    case O_LOAD:
    case O_NOT:
    case O_NEG:
        if (right || symbol || cv) error = EXERR_EXTRA;
        if (!left) error |= EXERR_MISSING;
        break;
    /* binary operators */
    case O_OR: case O_AND: case O_XOR:
    case O_ADD: case O_SUB: case O_MUL: case O_DIV:
    case O_LSHIFT: case O_RSHIFT:
    case O_LAND: case O_LOR: case O_LNOT:
        if (symbol || cv) error = EXERR_EXTRA;
        if (!left || !right) error |= EXERR_MISSING;
        break;
    default:
        printf("make_expr: bad type %d\n", type);
        break;
    }
    if (error) {
        printf("make_expr: bad expression %x\n", error);
        printf("%s %d %x %x %x %llx\n", opname[type], type,
                left, right, symbol, cv);
    }
    return (ep);
}

dump_expr(expr *ep, int level, char *prefix)
{
    printf("\n%*s: ", level * 4, prefix);
    if (!ep) {
        printf("NULL");
        return;
    }
    printf("%s(%d)", opname[ep->op], ep->width);
    if (ep->left) {
        dump_expr(ep->left, level + 1, "left");
    }
    if (ep->right) {
        dump_expr(ep->right, level + 1, "right");
    }
    if (ep->symbol) {
        printf(" %s", ep->symbol->name);
    }
    if (ep->op == O_CONST) {
        printf(" 0x%llx", ep->val);
    }
    if (ep->op == O_ARG) {
        printf(" %lld", ep->val);
    }
    if (ep->op == O_REG) {
        printf(" %s", regname[ep->val]);
    }
}

function *
make_function(label *label)
{
    function *fp;

    fp = malloc(sizeof (*fp));
    fp->label = label;
    fp->argcount = 0;
    fp->args = NULL;
    fp->ret = NULL;
    fp->next = funcs;

    fp->code = NULL;
    fp->tail = NULL;
    fp->sect = label->sect;

    funcs = fp;
}

dump_function(function *fp)
{
    expr *ep;
    code *cp;
    int ac = 0;
    char abuf[20];

    printf("function %s\n", fp->label->name);
    printf("args: %d\n", fp->argcount);
    for (ep = fp->args; ep; ep = ep->next) {
        sprintf(abuf, "arg %d", ac++);
        dump_expr(ep, 1, abuf);
    }
    printf("code:\n");
    for (cp = fp->code ; cp; cp = cp->next) {
        dump_code(cp);
    }
}

dump_functions()
{
    function *fp;
    for (fp = funcs; fp; fp = fp->next) {
        dump_function(fp);
    }


}

/*
 * return a complete tree of cracked instructions
 * this is assumed to recurse at each conditional branch
 */
inst *
crack_code(section *sp, int offset)
{
    inst *head;     /* what we're returning */
    inst *pip;      /* previous instr */
    inst *ip;       /* our new instr */
    int bytes = 4;
    label *lp;

    head = 0;

    do {
        /* have we been here before? */
        ip = lookup_inst(sp, offset);
        if (ip) {
            if (verbose & VERBOSE_FLOW) {
                printf("crack_code: fall into %x\n", offset);
            }
            bytes = 0;
        } else {    /* chew on some object code */
            ip = crack_inst(sp, offset, &bytes);
            if (verbose & VERBOSE_INST) {
                dump_inst(ip);
            }
            offset += bytes;
        }
        /* link it in */
        if (!head) {
            head = ip;
        } else {
            ip->flow[ip->refcnt++] = pip;
            pip->next = ip;
        }
        pip = ip;

        /* we also blow out here if we hit an external symbol */
        if ((lp = lookup_label(sp, offset)) && !lp->generated) {
            bytes = 0;
        }
    } while (bytes);

    return head;
}

void
decode()
{
    section *sp;
    int offset;
    int bytes;
    label *lp;
    function *fp;

    sp = get_section(".text");

    /*
     * go through all the external text labels, starting disassembly at each one until
     * we hit an unconditional control stop
     */
    for (lp = labels; lp; lp = lp->g_next) {
        /* skip non-text symbols */
        if (lp->sect != sp) continue;

        /* skip generated labels */
        if (lp->generated) continue;

        offset = lp->offset;
        if (verbose & VERBOSE_OBJ) {
            printf("process text label %s 0x%x\n", lp->name, offset);
        }

        curfunc = fp;
        fp = make_function(lp);
        fp->tree = crack_code(sp, offset);
        if (verbose & VERBOSE_OBJ) {
            printf("----------------------------------------------\n");
        }
    }
}

analyze_functions()
{
    function *fp;

    /* then, go through every function, and build up an emittable code tree */
    for (fp = funcs; fp; fp = fp->next) {
        analyze_function(fp);
    }
}

void
allocate_section(bfd *bp, asection *asp, void *obj)
{
    section *sp;
    int i;

    /* ignore sections that don't correspond to address spaces */
    if (!(asp->flags & SEC_ALLOC))
	return;

    sp = get_section(asp->name);
    section_check_size(sp, asp->size);

}

void
create_section(bfd *bp, asection *asp, void *obj)
{
    reference *rfp;
    asymbol *symbol;
    section *sp;
    arelent **relocs, *rp;
    int i;
    int nreloc;
    label *lp;

    /* ignore sections that don't correspond to address spaces */
    if (!(asp->flags & SEC_ALLOC))
        return;

    sp = get_section(asp->name);
    sp->length = asp->size;
    if (asp->flags & SEC_CODE) {
        sp->text = 1;
    }

    /*
     * read in the section
     */
    bfd_malloc_and_get_section(bp, asp, (bfd_byte **)&sp->data);

    /*
     * process the relocs
     */
    i = bfd_get_reloc_upper_bound (bp, asp);
    if (i <= 0) {
        bfd_perror("get reloctab");
        return;
    }
    relocs = malloc(i);
    nreloc = bfd_canonicalize_reloc(bp, asp, relocs, (asymbol **)obj);
    if (verbose & VERBOSE_OBJ) {
        printf("create_section: %s process %d relocs\n", asp->name, nreloc);
    }
    for (i = 0; i < nreloc; i++) {
        rp = relocs[i];
        symbol = *rp->sym_ptr_ptr;

        lp = make_label(get_section(symbol->section->name),
			symbol->value + rp->addend, NULL);

        rfp = make_reference(sp, rp->address, howtoindex(rp->howto->type), lp);
        rfp->howcode = rp->howto->type;
    }
}
int
get_labeldef(char *def, char **nptr, char **sptr, long long *vptr)
{
    int i;
    i = sscanf(def, "%m[^=]=%m[^:]:%lli", nptr, sptr, vptr);
    if (i != 3) {
        i = sscanf(def, "%m[^=]=%lli", nptr, vptr);
        if (i != 2) {
            printf("malformed label spec: <label>=[<section>:]<value>\n");
            return (0);
        }
        *sptr = ".text";
    }
    if (verbose) {
        printf("get_labeldef: %s = %s:0x%llx\n", *nptr, *sptr, *vptr);
    }
    return (1);
}

int
get_jtabledef(char *def, char **nptr, long long *vptr, int *lenp)
{
    int i;
    *lenp = 0;

    i = sscanf(def, "%m[^=]=%lli,%d", nptr, vptr, lenp);
    if (i < 2) {
        printf("malformed jump table spec: <label>=<value>[,<len>]\n");
        return (0);
    }
    if (verbose) {
        printf("getjtabledef: %d %s %llx %d\n", i, *nptr, *vptr, *lenp);
    }
    return (1);
}

void
read_directive_file(char *fn)
{
    int i;
    char *s;
    FILE *df;
#define IBUFLEN 255
    char ibuf[IBUFLEN+1];

    char *name;
    char *sect;
    long long addr;
    int len;

    s = rindex(fn, '.');
    if (s) {
        i = s - fn;
    } else {
        i = strlen(fn);
    }
    s = malloc(i + 3);
    sprintf(s, "%.*s.d", i, fn);

    df = fopen(s, "r");
    if (!df) {
        perror(s);
        return;
    }

    while (fgets(ibuf, IBUFLEN, df)) {
        if (ibuf[0] == '#')
            continue;
        if (strncmp(ibuf, "jump", 4) == 0) {
            if (get_jtabledef(&ibuf[4], &name, &addr, &len)) {
            }
        } else if (strncmp(ibuf, "label", 5) == 0) {
            if (get_labeldef(&ibuf[5], &name, &sect, &addr)) {
                make_label(get_section(sect), addr, name);
            }
        } else {
            printf("unknown directive %s\n", ibuf);
        }
    }

    fclose(df);
}

/*
 * decompile a file
 */
void
decomp(char *infilename)
{
    bfd *bfdp;
    int i;
    int nsyms;
    asymbol **symbol, *sp;

    /*
     * open and sanity check
     */
    bfdp = bfd_openr(infilename, "elf64-sparc");
    if (!bfdp) {
        bfd_perror(infilename);
        exit(-1);
    }
    if (!bfd_check_format_matches(bfdp, bfd_object, 0)) {
        return;
    }

    if (directive) {
        read_directive_file(infilename);
    }

    bfd_map_over_sections(bfdp, allocate_section, symbol);

    /*
     *  create our initial batch of labels from the symbol table
     */
    i = bfd_get_symtab_upper_bound(bfdp);
    if (i <= 0) {
        bfd_perror("get symtab");
        return;
    }
    symbol = malloc (i);
    nsyms = bfd_canonicalize_symtab(bfdp, symbol);
    if (nsyms < 0) {
        bfd_perror("canonicalize symtab");
    }
    if (verbose & VERBOSE_FUNC) {
        printf("decomp: process %d globals\n", nsyms);
    }

    for (i = 0; i < nsyms; i++) {
        sp = symbol[i];

        if (bfd_is_und_section(sp->section) ||
            bfd_is_com_section(sp->section)) {
            static int undef_hack = 0;
            sp->value = undef_hack;
            undef_hack += 4;
        } else {
            if (!(sp->section->flags & SEC_ALLOC)) {
                continue;
            }
            if (!(sp->flags & (BSF_FUNCTION|BSF_OBJECT))) {
                continue;
            }
        }
        make_label(get_section(sp->section->name), sp->value, sp->name);
    }

    /*
     * iterate over all sections, snarfing in data and relocs
     */
    bfd_map_over_sections(bfdp, create_section, symbol);

    free(symbol);

    bfd_close(bfdp);

    /*
     * now that we have all the externally provided information,
     * let's analyze the machine code
     */
    decode();
    analyze_functions();
    dump_sections();
    dump_functions();
    free_decomp();
}

struct option options[] = {
    {"verbose", optional_argument, 0, 'v'},
    {"bias", required_argument, 0, 'b'},
    {"max", required_argument, 0, 'm'},
    {"label", required_argument, 0, 'l'},
    {"jump", required_argument, 0, 'j'},
    {0, 0, 0, 0}
};

usage()
{
    printf("Usage: %s <option(s)> <file>\n", pname);
    printf("\
-v --verbose\tbe noisy\n\
-b --bias=<address>\tassume that file is loaded at address <bias>\n\
-m --max=<limit>\tstop decompilation after address <limit>\n\
-l --label <label>=[<section>:]<data address>\tspecify an address label\n\
-j --jump <label>=<jump table address>[,<length>]\tspecify a jump table address\n\
-d\tread a directive file\n\
");
    exit(-1);
}


int
main(int argc, char **argv)
{
    char *infile;
    opterr = 0;
    int index;
    int option_index = 0;
    char c;
    long long laddr;
    char *lname;
    int jlen;
    char *lsec;

    pname = argv[0];

    while (1) {
        c = getopt_long (argc, argv, "tdv::b:m:l:j:", options, &option_index);

        if (c == -1)
            break;

        switch (c) {
            case 't':
#ifdef UNIT_TEST
                test();
#endif
                break;
            case 'v':
                if (optarg) {
                    if (*optarg == '=') optarg++;
                    verbose = strtol(optarg, 0, 0);
                } else {
                    verbose++;
                }
                if (verbose) {
                    int i = verbose;
                    setlinebuf(stdout);
                    printf("verbosity: 0x%x (", verbose);
#define VD(s) if (verbose & VERBOSE_##s) { \
    printf("%s=%x", #s, VERBOSE_##s); i ^= VERBOSE_##s; if (i) printf(" "); }
                    VD(FUNC);
                    VD(ANAL);
                    VD(OBJ);
                    VD(INST);
                    VD(LOW);
                    VD(FLOW);
                    VD(FOLD);
                    VD(CALC);
                    printf(")\n");
                }
                break;

            case 'b':
                if (*optarg == '=') optarg++;
                bias = strtol(optarg, 0, 0);
                break;

            case 'd':
                directive++;
                break;

            case 'm':
                imax = strtol(optarg, 0, 0);
                break;

            case 'l':
                get_labeldef(optarg, &lname, &lsec, &laddr);
                make_label(get_section(lsec), laddr, lname);
                break;

            case 'j':
                get_jtabledef(optarg, &lname, &laddr, &jlen);
                break;

            default:
            case '?':
                usage();
                break;
        }
    }

    bfd_init();
    expr_init();

    for (index = optind; index < argc; index++) {
	    infile = argv[index];

        if (access(infile, R_OK) != 0) {
            perror(infile);
            continue;
        }
        decomp(infile);
        exit(0);
    }
}
